package service

import (
	"errors"
	"github.com/gin-gonic/gin"
	"github.com/redis/go-redis/v9"
	"gorm.io/gorm"
	"seat-service/constant"
	"seat-service/initialization"
	"seat-service/model"
	"seat-service/model/dto"
	"strconv"
	"time"
)

type ReserveService struct {
}

func (*ReserveService) GetDuration(sid uint, seatId uint, t int) bool {
	total := 0
	initialization.DB.Select("sum(TIMESTAMPDIFF(minute,appointment_time_start,appointment_time_end))").Where("student_id = ? and seat_id = ? and reserve_status = 1", sid, seatId).Scan(&total)
	if total+t > 16*60 {
		return false
	}
	return true
}

func (*ReserveService) GetClash(sid uint, start time.Time, end time.Time) (reserves []model.Reserve, err error) {
	err = initialization.DB.Where("reserve_status = 1 and student_id = ? and(? BETWEEN appointment_time_start AND appointment_time_end OR ? BETWEEN appointment_time_start AND appointment_time_end) ", sid, start, end).Find(&reserves).Error
	return
}

func (*ReserveService) ReserveCount(reserve *model.Reserve) (count int64, err error) {
	err = initialization.DB.Model(&model.Reserve{}).Where("seat_id = ? and(? BETWEEN appointment_time_start AND appointment_time_end OR ? BETWEEN appointment_time_start AND appointment_time_end OR appointment_time_start BETWEEN ? AND ? OR appointment_time_end BETWEEN ? AND ?)",
		reserve.SeatId, reserve.AppointmentTimeStart, reserve.AppointmentTimeEnd, reserve.AppointmentTimeStart, reserve.AppointmentTimeEnd, reserve.AppointmentTimeStart, reserve.AppointmentTimeEnd).Where("reserve_status = 1").Count(&count).Error
	return
}

func (*ReserveService) QueryClashByTime(par model.Reserve, time time.Time) (reserve []model.Reserve, err error) {
	err = initialization.DB.Where(par).Where("? >= appointment_time_start AND ? <= appointment_time_end", time, time).Find(&reserve).Error
	return
}

func (*ReserveService) Update(par model.Reserve) (err error) {
	err = initialization.DB.Where("id = ?", par.ID).Updates(&par).Error
	return
}

func (*ReserveService) Query(par model.Reserve) (reserve []model.Reserve, err error) {
	err = initialization.DB.Where("student_id = ? and classroom_id = ? and seat_id = ?", par.StudentId, par.ClassroomID, par.SeatId).Where("DATE(appointment_time_start) = ?", par.AppointmentTimeStart.Format("2006-01-02")).Find(&reserve).Error
	return
}

func (*ReserveService) SignIn(par *dto.SignInParam) (err error) {
	err = initialization.DB.Where("classroom_id = ?", par.ClassroomID).Error
	return
}

func (*ReserveService) Get(id uint) (reserves []model.Reserve, err error) {
	err = initialization.DB.Where("student_id = ?", id).Preload("Classroom").Find(&reserves).Error
	return
}

func (*ReserveService) GetByReserve(reserve *model.Reserve) (err error) {
	err = initialization.DB.Where(reserve).Where("reserve_status = 1").Find(reserve).Error
	return
}

func (*ReserveService) Save(reserve *model.Reserve) (err error) {
	err = initialization.DB.Save(reserve).Error
	return
}

func (*ReserveService) CreateWithTransaction(tx *gorm.DB, reserve *model.Reserve) (model.Reserve, error) {
	err := tx.Create(reserve).Error
	return *reserve, err
}

func (*ReserveService) GetByReserveParam(param dto.ParamTimestamp) (reserves []model.Reserve, err error) {
	for _, par := range param.Times {
		tmp := []model.Reserve{}
		s := time.Unix(par.Start, 0)
		e := time.Unix(par.End, 0)
		err = initialization.DB.
			Where("classroom_id = ? AND ((appointment_time_start BETWEEN ? AND ? OR appointment_time_end BETWEEN ? AND ? )OR (appointment_time_start < ? AND appointment_time_end > ?))", param.ClassroomID, s, e, s, e, s, e).Find(&tmp).Error
		reserves = append(reserves, tmp...)
	}
	return
}

func (*ReserveService) SaveRedisReserve(r model.Reserve, context *gin.Context, id uint, appointment time.Time) (err error) {
	uuid := appointment.Format("2006-01-02 15:04")
	key := constant.Reserve + "/" + strconv.Itoa(int(id)) + "/" + uuid //用学生id + 此次预约开始时间为key
	futureTime := appointment.Add(10 * time.Minute)                    //开始预约的时间的10分钟后
	value := futureTime.Format("2006-01-02 15:04")                     //需要签到的时间
	//数据会在延时队列中处理,不需要设置过期时间
	err = initialization.Redis.Set(context, key, value, 0).Err()
	//fmt.Println("需要签到的最晚时间", futureTime)
	//将预约信息添加到redis中
	initialization.Redis.ZAdd(context, constant.Sign, redis.Z{
		Score:  float64(futureTime.Unix()),                            //需要签到的最晚时间
		Member: strconv.Itoa(int(id)) + "," + strconv.Itoa(int(r.ID)), //学生id , 预约单id
	})
	initialization.Redis.ZAdd(context, constant.Appointment, redis.Z{
		Score:  float64(appointment.Unix()),         //预约开始时间
		Member: key + "," + strconv.Itoa(int(r.ID)), //加一个预约单id 用于取消预约后的处理
	})
	return
}

func (*ReserveService) DeleteRedisReserve(context *gin.Context, id int, uuid string) (err error, isExist bool) {
	key := constant.Reserve + "/" + strconv.Itoa(id) + "/" + uuid
	//检测key是否存在
	exists, err := initialization.Redis.Exists(context, key).Result()
	if exists == 1 {
		//存在 可以签到
		isExist = true
		err = initialization.Redis.Del(context, key).Err()
	} else {
		isExist = false
	}
	return
}

func (*ReserveService) CheckStudentStatus(id uint) (student model.Student, err error) {
	//根据id 查询学生是否被封禁
	err = initialization.DB.Where("id = ?", id).Find(&student).Error
	if err != nil {
		return
	}
	//如果当前学生处于封禁状态 1为违规
	studentStatus := student.StudentStatus
	if studentStatus == 1 {
		//如果当前学生处于违规状态 , 判断当前时间是否超过了锁定结束时间
		now := time.Now().Unix()                           //获取当前时间的时间戳
		lockEndTime := student.ViolationLockTimeEnd.Unix() //获取锁定结束时间的时间戳
		if now > lockEndTime {
			//锁定时间结束 , 修改用户状态 , 清除锁定时间 学生同样可以正常预约
			err = initialization.DB.Where("id = ?", id).Model(model.Student{}).
				Updates(map[string]interface{}{"student_status": 0, "violation_lock_time_start": nil, "violation_lock_time_end": nil}).Error
		} else {
			//返回当前学生的信息 , 以及违规状态
			return student, errors.New("StudentISViolate")
		}
	}
	//如果学生能够正常预约,返回空
	return model.Student{}, nil
}

func (*ReserveService) Renewal(id uint, s, e time.Time) ([]model.Reserve, error) {
	t := time.Now()
	res := model.Reserve{}
	err := initialization.DB.Model(&model.Reserve{}).Where("student_id =? AND appointment_time_start>? AND appointment_time_end<?", id, t, t).Scan(&res).Error
	if err != nil {
		return nil, err
	}
	if res.ID == 0 {
		return nil, errors.New("0")
	}

	sliceReserve := make([]model.Reserve, 0)
	err = initialization.DB.Model(&model.Reserve{}).Where("seat_id=? AND appointment_time_start>? AND appointment_time_end<?", res.SeatId, s, e).Find(&sliceReserve).Error
	if err != nil {
		return nil, err
	}
	if len(sliceReserve) != 0 {
		return sliceReserve, nil
	}
	res.AppointmentTimeEnd = e
	err = initialization.DB.Model(&model.Reserve{}).Updates(res).Error
	if err != nil {
		return nil, err
	}
	return nil, nil
}
func (*ReserveService) AppealQueryUserID(id uint) (uint, error) {
	res := model.Reserve{}
	t := time.Now()
	err := initialization.DB.Model(&model.Reserve{}).Where("seat_id=? AND appointment_time_start<? AND appointment_time_end>?", id, t, t).Scan(res).Error
	if err != nil {
		return 0, err
	}

	return res.StudentId, nil
}

func (*ReserveService) AppealQueryStudent(studentID uint) (model.Student, error) {

	stu := model.Student{}
	err := initialization.DB.Model(&model.Student{}).Where("id=?", studentID).Scan(stu).Error
	if err != nil {
		return model.Student{}, err
	}
	return stu, nil
}

func (*ReserveService) AppealQuerySeatNum(seatID uint) (string, error) {
	st := model.Seat{}
	err := initialization.DB.Model(model.Seat{}).Where("id=?", seatID).Scan(st).Error
	if err != nil {
		return "", err
	}
	return st.SeatNumber, nil
}

func (*ReserveService) AppealSuccess(seatID uint) error {
	err := initialization.DB.Model(&model.Reserve{}).Where("seat_id=?", seatID).Update("reserve_status", "2").Error
	return err
}
